package com.dcjt.firehotcarpad.utils

import android.content.Context
import android.os.Handler
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.source.ConcatenatingMediaSource
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.util.Util
import java.util.*
import kotlin.math.roundToInt

/**
 * ExoPlayer工具类
 */
class ExoPlayer(context: Context) {

    private val handler = Handler()
    private val formatBuilder = StringBuilder()
    private val formatter:Formatter
    private var adGroupTimesMs = longArrayOf(0)
    private var playedAdGroups = booleanArrayOf(false)
    private val controlDispatcher = DefaultControlDispatcher()
    private val period = Timeline.Period()
    private val window = Timeline.Window()
    private val simpleExoPlayer:SimpleExoPlayer
    private val defaultDataSourceFactory:DefaultDataSourceFactory
    private var position = 0
    private var curUri:String = ""
    var listener:AudioControlListener? = null

    init {
        formatter = Formatter(formatBuilder, Locale.getDefault())
        simpleExoPlayer = SimpleExoPlayer.Builder(context).build()
        defaultDataSourceFactory = DefaultDataSourceFactory(context, "audio/mpeg")
        val concatenatingMediaSource = ConcatenatingMediaSource()
        concatenatingMediaSource.addMediaSources(arrayListOf())
        simpleExoPlayer.playWhenReady = false
        simpleExoPlayer.addListener(object : Player.EventListener{
            override fun onIsLoadingChanged(isLoading: Boolean) {
                super.onIsLoadingChanged(isLoading)
                if (isLoading) {
                    handler.post(loadStatusRunnable)
                }
                listener?.isPlay(position,isLoading)
            }

            override fun onPlayerError(error: ExoPlaybackException) {
                super.onPlayerError(error)
                listener?.onPlayError(error)
            }

        })
    }

    fun isPlay():Boolean{
        return simpleExoPlayer.isPlaying
    }

    fun onPrepare(uri:String){
        if(uri == curUri)return
        curUri = uri
        val mediaSource =  ProgressiveMediaSource.Factory(defaultDataSourceFactory).createMediaSource( MediaItem.Builder().setUri(curUri).build())
        simpleExoPlayer.setMediaSource(mediaSource)
        simpleExoPlayer.prepare()
    }

    fun onStart(position:Int){
        this.position = position
        if (simpleExoPlayer.playbackState == Player.STATE_ENDED) {
            //重新播放
            controlDispatcher.dispatchSeekTo(simpleExoPlayer, simpleExoPlayer.currentWindowIndex, Long.MIN_VALUE + 1)
        }
        controlDispatcher.dispatchSetPlayWhenReady(simpleExoPlayer, true)
        listener?.isPlay(position,true)
    }

    fun onPause(){
        controlDispatcher.dispatchSetPlayWhenReady(simpleExoPlayer, false)
        listener?.isPlay(position, false)
    }
    fun release(){
        simpleExoPlayer.release()
    }
    fun getPosition():Int = position
    fun seekToTimeBarPosition(positionMs:Long){
        var ms = positionMs
        val timeline = simpleExoPlayer.currentTimeline
        var windowIndex = 0
        if(!timeline.isEmpty){
            val windowCount = timeline.windowCount
            while (true) {
                val windowDurationMs = timeline.getWindow(windowIndex, window).durationMs
                if (ms < windowDurationMs) {
                    break
                } else if (windowIndex == windowCount - 1) {
                    // Seeking past the end of the last window should seek to the end of the timeline.
                    ms = windowDurationMs
                    break
                }
                ms -= windowDurationMs
                windowIndex++
            }
        } else {
            windowIndex = simpleExoPlayer.currentWindowIndex
        }
        val dispatched = controlDispatcher.dispatchSeekTo(simpleExoPlayer, windowIndex, ms)
        if (!dispatched) {
            handler.post(loadStatusRunnable)
        }
    }
    private val loadStatusRunnable = object :Runnable{
        override fun run() {
            var durationUs: Long = 0
            var adGroupCount = 0
            var currentWindowTimeBarOffsetMs: Long = 0
            val currentTimeline = simpleExoPlayer.currentTimeline
            if(!currentTimeline.isEmpty){
                val currentWindowIndex = simpleExoPlayer.currentWindowIndex
                val firstWindowIndex = currentWindowIndex
                val lastWindowIndex = currentWindowIndex
                for (i in firstWindowIndex..lastWindowIndex){
                    if( i == currentWindowIndex){
                        currentWindowTimeBarOffsetMs = C.usToMs(durationUs)
                    }
                    currentTimeline.getWindow(i,window)
                    if(window.durationUs == C.TIME_UNSET)break
                    for (j in window.firstPeriodIndex .. window.lastPeriodIndex){
                        currentTimeline.getPeriod(j,period)
                        val periodAdGroupCount = period.adGroupCount
                        for (adGroupIndex in 0 until periodAdGroupCount){
                            var adGroupTimeInPeriodUs = period.getAdGroupTimeUs(adGroupIndex)
                            if(adGroupTimeInPeriodUs == C.TIME_END_OF_SOURCE){
                                if(period.durationUs == C.TIME_UNSET)continue
                                adGroupTimeInPeriodUs = period.durationUs
                            }
                            val adGroupTimeInWindowUs = adGroupTimeInPeriodUs + period.positionInWindowUs
                            if (adGroupTimeInWindowUs >= 0 && adGroupTimeInWindowUs <= window.durationUs) {
                                if (adGroupCount == adGroupTimesMs.size) {
                                    val newLength = if(adGroupTimesMs.isEmpty()) 1 else adGroupTimesMs.size*2
                                    adGroupTimesMs = adGroupTimesMs.copyOf(newLength)
                                    playedAdGroups = playedAdGroups.copyOf(newLength)
                                }
                                adGroupTimesMs[adGroupCount] = C.usToMs(durationUs + adGroupTimeInWindowUs)
                                playedAdGroups[adGroupCount] = period.hasPlayedAdGroup(adGroupIndex)
                                adGroupCount++
                            }
                        }
                    }
                    durationUs += window.durationUs
                }
            }
            durationUs = C.usToMs(window.durationUs)
            val  currentTime = currentWindowTimeBarOffsetMs + simpleExoPlayer.contentPosition
            val  bufferedPosition = currentWindowTimeBarOffsetMs + simpleExoPlayer.contentBufferedPosition
            listener?.setCurTimeString(position,"" + Util.getStringForTime(formatBuilder, formatter, currentTime))
            listener?.setDurationTimeString(position, "" + Util.getStringForTime(formatBuilder, formatter, durationUs))
            listener?.setBufferedPositionTime(position, bufferedPosition)
            listener?.setCurPositionTime(position, currentTime)
            listener?.setDurationTime(position, durationUs)
            handler.removeCallbacks(this)
            val playbackState = simpleExoPlayer.playbackState
            if(playbackState!=Player.STATE_IDLE && playbackState!=Player.STATE_ENDED){
                //播放器未开始播放后者播放器播放结束
                if(playbackState!=Player.STATE_IDLE&&playbackState!=Player.STATE_ENDED){
                    val delayMs: Long
                    //当正在播放状态时
                    if(simpleExoPlayer.playWhenReady &&playbackState==Player.STATE_READY){
                        val playBackSpeed = simpleExoPlayer.playbackParameters.speed
                        when {
                            playBackSpeed<=0.1f -> delayMs=1000
                            playBackSpeed<=5f -> {
                                //中间更新周期时间
                                val mediaTimeUpdatePeriodMs= 1000 / 1.coerceAtLeast((1 / playBackSpeed).roundToInt())
                                //当前进度时间与中间更新周期之间的多出的不足一个中间更新周期时长的时间
                                val surplusTimeMs = currentTime % mediaTimeUpdatePeriodMs
                                //播放延迟时间
                                var mediaTimeDelayMs=mediaTimeUpdatePeriodMs-surplusTimeMs
                                if(mediaTimeDelayMs<(mediaTimeUpdatePeriodMs/5)){
                                    mediaTimeDelayMs+=mediaTimeUpdatePeriodMs
                                }
                                delayMs = if(playBackSpeed == 1f) mediaTimeDelayMs else mediaTimeDelayMs/playBackSpeed.toLong()
                            }
                            else -> delayMs=200
                        }
                    }else {
                        //当暂停状态时
                        delayMs=1000
                    }
                    handler.postDelayed(this,delayMs)
                }else{
                    listener?.isPlay(position,false)
                }
            }
        }

    }

    interface AudioControlListener {
        // 设置当前进度
        fun setCurPositionTime(position: Int, curPositionTime: Long )

        // 设置总时长
        fun setDurationTime(position: Int, durationTime: Long)
        fun setBufferedPositionTime(position: Int, bufferedPosition: Long)

        // 设置当前时间
        fun setCurTimeString(position: Int, curTimeString: String)
        fun isPlay(position: Int, isPlay: Boolean)
        fun setDurationTimeString(position: Int,durationTimeString: String)
        fun onPlayError(e:ExoPlaybackException)
    }
}